package com.suray.wcs.service.thread.process;

import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.enums.Direction;
import com.suray.basic.wcs.plc.pojo.oper.single.DeviceInterface;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.wcs.service.enums.TaskType;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.thread.process.util.SingleDeviceStatus;

import java.io.IOException;

public abstract class SingelDeviceProcess {
    /**
     * 当前进程状态
     */
    protected SingleDeviceStatus processStatus;

    /**
     * 起点设备
     */
    protected DeviceInterface startDevice;

    /**
     * 终点设备
     */
    protected DeviceInterface endDevice;

    /**
     * 入库任务
     */
    protected boolean taskIn;

    /**
     * 设备转动方向
     */
    protected Direction direction;

    protected TaskDB taskDB;

    /**
     * 流程是否结束
     */
    protected boolean endProcess;

    /**
     * 起点设备是否需要出货之前执行动作
     */
    protected boolean needStartPreAction;

    /**
     * 终点设备是否需要入货之前执行动作
     */
    protected boolean needEndPreAction;

    /**
     * 起点设备是否需要出货之后执行动作
     */
    protected boolean needStartAftAction;

    /**
     * 终点设备是否需要入货之后执行动作
     */
    protected boolean needEndAftAction;

    private int timeOut;

    /**
     * 单体设备流程
     * @param startDevice : 起点设备
     * @param endDevice : 终点设备
     * @param taskDB : 任务
     * @param needStartPreAction : 起点设备是否需要出货之前执行动作
     * @param needEndPreAction : 终点设备是否需要入货之前执行动作
     * @param needStartAftAction : 起点设备是否需要出货之后执行动作
     * @param needEndAftAction : 终点设备是否需要入货之后执行动作
     * @param timeOut : 等待超时时间
     */
    public SingelDeviceProcess(DeviceInterface startDevice, DeviceInterface endDevice, TaskDB taskDB,
                               boolean needStartPreAction, boolean needEndPreAction, boolean needStartAftAction,
                               boolean needEndAftAction, int timeOut) {
        if (startDevice == null) {
            throw new RuntimeException("起点设备不能为空");
        }

        if (endDevice == null) {
            throw new RuntimeException("终点设备不能为空");
        }

        if (taskDB.getTaskType() == TaskType.IN) {
            direction = Direction.IN;
            this.taskIn = true;
        } else {
            direction = Direction.OUT;
            this.taskIn = false;
        }

        this.startDevice = startDevice;
        this.endDevice = endDevice;
        this.taskDB = taskDB;
        this.endProcess = false;
        this.needEndPreAction = needStartPreAction;
        this.needStartPreAction = needEndPreAction;
        this.needStartAftAction = needStartAftAction;
        this.needEndAftAction = needEndAftAction;
        this.processStatus = SingleDeviceStatus.INIT;
        this.timeOut = timeOut;
    }

    public void setProcessStatus(SingleDeviceStatus processStatus) {
        this.processStatus = processStatus;
    }

    /**
     * 启动
     */
    public void start() {
        LoggerUtil.info(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + " 输送开始", this.getClass());
        process();
    }

    private void process() {
        String interruptStr = startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + ":";
        while (true) {
            try {
                if (taskDB != null) {
                    Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), interruptStr + processStatus + " 中断");
                }
                deviceRunProcess();

                if (endProcess) {
                    break;
                }
            } catch (IOException e) {
                WcsFuncUtil.exceptionLog(e, this.getClass());
                LoggerUtil.error(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() +
                        " 通讯异常，流程进度：" + processStatus, this.getClass());
            }

            WcsFuncUtil.delay(1000);
        }
    }

    private void checkTimeOut(int timeOut) {
        if (timeOut <= 0) {
            throw new RuntimeException(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + ":" +
                    processStatus + "时 执行超时");
        }
    }

    /**
     * 自动横移机流程
     */
    private void deviceRunProcess() throws IOException {
        switch (processStatus) {
            case INIT:
                init();
                processStatus = SingleDeviceStatus.WAIT_DEVICE_PERMIT_RUN;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case WAIT_DEVICE_PERMIT_RUN:
                //等待设备状态
                checkTimeOut(--timeOut);
                if (!waitDevicePermitRun()) {
                    break;
                }
                processStatus = SingleDeviceStatus.START_DEVICE_PRE_ACTION;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case START_DEVICE_PRE_ACTION:
                //起点设备动作
                checkTimeOut(--timeOut);
                if (needStartPreAction) {
                    if (!startDevicePreAction()) {
                        break;
                    }
                }
                processStatus = SingleDeviceStatus.END_DEVICE_PRE_ACTION;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", needStartPreAction?" + needStartPreAction,
                        this.getClass());
                timeOut = 60;
            case END_DEVICE_PRE_ACTION:
                //终点设备动作
                checkTimeOut(--timeOut);
                if (needEndPreAction) {
                    if (!endDevicePreAction()) {
                        break;
                    }
                }
                processStatus = SingleDeviceStatus.WAIT_DEVICE_PRE_ACTION_END;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", needEndPreAction?" + needEndPreAction,
                        this.getClass());
                timeOut = 60;
            case WAIT_DEVICE_PRE_ACTION_END:
                //等待门开启完成
                checkTimeOut(--timeOut);
                if (needStartPreAction || needEndPreAction) {
                    if (!waitDevicePreActionEnd()) {
                        break;
                    }
                }
                processStatus = SingleDeviceStatus.END_DEVICE_IN_CARGO;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case END_DEVICE_IN_CARGO:
                //后面的设备先转动
                if (!endDeviceInCargo()) {
                    break;
                }
                processStatus = SingleDeviceStatus.START_DEVICE_OUT_CARGO;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case START_DEVICE_OUT_CARGO:
                //前面的设备转动
                if (!startDeviceOutCargo()) {
                    break;
                }
                processStatus = SingleDeviceStatus.WAIT_DEVICE_RUN_END;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case WAIT_DEVICE_RUN_END:
                //等待设备转动完成
                checkTimeOut(--timeOut);
                if (!waitDeviceRunEnd()) {
                    break;
                }
                processStatus = SingleDeviceStatus.START_DEVICE_AFT_ACTION;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case START_DEVICE_AFT_ACTION:
                //关门
                if (needStartAftAction) {
                    if (!startDeviceAftAction()) {
                        break;
                    }
                }
                processStatus = SingleDeviceStatus.END_DEVICE_AFT_ACTION;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", needStartAftAction?" + needStartAftAction,
                        this.getClass());
                timeOut = 60;
            case END_DEVICE_AFT_ACTION:
                //关门
                if (needEndAftAction) {
                    if (!endDeviceAftAction()) {
                        break;
                    }
                }
                processStatus = SingleDeviceStatus.WAIT_DEVICE_AFT_ACTION_END;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", needEndAftAction?" + needEndAftAction,
                        this.getClass());
                timeOut = 60;
            case WAIT_DEVICE_AFT_ACTION_END:
                //等待门关闭完成，如果需要等待外部直接调取waitCloseStartDoor，这里默认不需要等待
                checkTimeOut(--timeOut);
                if (needStartAftAction || needEndAftAction) {
                    if (!waitDeviceAftActionEnd()) {
                        break;
                    }
                }
                processStatus = SingleDeviceStatus.END;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case END:
                end();
                break;
            default:
                throw new RuntimeException(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() +
                        " 进度错误：" + processStatus);
        }
    }

    /**
     * 初始化
     */
    private void init() {
        if (!startDevice.hasCargo()) {
            throw new RuntimeException(startDevice.getDeviceName() + " 无货，无法输送货物至 " + endDevice.getDeviceName());
        }
    }

    /**
     * 等待设备允许运行
     * @return
     */
    protected abstract boolean waitDevicePermitRun() throws IOException;

    /**
     * 起点设备开始前动作
     * @return
     */
    protected abstract boolean startDevicePreAction() throws IOException;

    /**
     * 终点设备开始前动作
     * @return
     */
    protected abstract boolean endDevicePreAction() throws IOException;

    /**
     * 等待设备开始动作完成
     * @return
     */
    protected abstract boolean waitDevicePreActionEnd() throws IOException;

    /**
     * 终点设备进货
     * @return
     */
    protected abstract boolean endDeviceInCargo() throws IOException;

    /**
     * 起点设备出货
     * @return
     */
    protected abstract boolean startDeviceOutCargo() throws IOException;

    /**
     * 等待货物输送完成
     * @return
     */
    protected abstract boolean waitDeviceRunEnd() throws IOException;

    /**
     * 起点设备结束动作
     * @return
     */
    protected abstract boolean startDeviceAftAction() throws IOException;

    /**
     * 终点设备结束动作
     * @return
     */
    protected abstract boolean endDeviceAftAction() throws IOException;

    /**
     * 等待设备结束动作完成
     * @return
     */
    protected abstract boolean waitDeviceAftActionEnd() throws IOException;

    /**
     * 结束
     */
    protected void end() {
        endProcess = true;
        LoggerUtil.info(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + " 输送任务完成",
                this.getClass());
    }

    /**
     * 流程是否结束
     * @return
     */
    public boolean isProcessEnd() {
        return processStatus == SingleDeviceStatus.END;
    }
}
